package com.marvels.financial.pay.kqpay.util;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.SSLContext;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.lang3.StringUtils;

import com.alibaba.fastjson.JSON;
import com.bill99.asap.exception.CryptoException;
import com.bill99.asap.service.ICryptoService;
import com.bill99.asap.service.impl.CryptoServiceFactory;
import com.bill99.schema.asap.commons.Mpf;
import com.bill99.schema.asap.data.SealedData;
import com.bill99.schema.asap.data.UnsealedData;
import com.marvels.common.constants.MarvelsConstant;
import com.marvels.common.dto.pay.McfeRepaymentTradeDetails;
import com.marvels.common.utils.MarvelsLogUtil;
import com.marvels.financial.pay.kqpay.dto.Pay2bankOrder;
import com.marvels.financial.pay.kqpay.dto.Pay2bankRequest;
import com.marvels.financial.pay.kqpay.dto.Pay2bankResponse;
import com.marvels.financial.pay.kqpay.dto.Pay2bankSearchDetail;
import com.marvels.financial.pay.kqpay.dto.Pay2bankSearchRequest;
import com.marvels.financial.pay.kqpay.dto.Pay2bankSearchRequestParam;
import com.marvels.financial.pay.kqpay.dto.Pay2bankSearchResponse;
import com.marvels.financial.pay.kqpay.dto.Pay2bankSearchResult;

import cn.hutool.core.util.RandomUtil;

/**
 * 发送交易到快钱
 * @author Administrator
 *
 */
public class PayToKuanQian {
	
	//接口版本
	private static String VERSION = "1.0";

	//字符编码
	private static String encoding = "UTF-8";
	
	//策略编码，固定值 F41
	private static String fetureCode = "F41";

	public static void main(String[] args) {
		//String URL = "https://www.99bill.com/fo-pay-query/pay2bank/query";
		String URL = "https://www.99bill.com/fo-pay/pay2bank/pay";
		String membercode = "10210467420";
		McfeRepaymentTradeDetails tradeDetail = new McfeRepaymentTradeDetails();
		String orderId = "KQDF"+ RandomUtil.randomString(21);
		//String orderId = "KQDFj5wu1pkin20y8eloixyvq";
		System.out.println("订单号：" + orderId);
		tradeDetail.setPayOrderNumber(orderId);
		tradeDetail.setBankName("建设银行");
		tradeDetail.setBankBranchName("");
		tradeDetail.setCustName("曾俊杰");
		tradeDetail.setMobileNo("18358466186");
		tradeDetail.setBankNumber("6217002030010792892");
		tradeDetail.setAmount("300000.00");
		// tradeDetail.setErrorMsg("模拟交易成功");
		tradeDetail.setCreatedDate(Calendar.getInstance().getTime());
		try {
			Map<String, Object> result = pay(membercode,URL,tradeDetail);
			System.out.println(result.toString());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static Map<String, Object> pay(String membercode,String url,McfeRepaymentTradeDetails tradeDetail) throws Exception {
		MarvelsLogUtil.getInstance().info(tradeDetail.getPayOrderNumber()+"调用快钱代付商户号=" + membercode+ ",请求地址="+url);
		MarvelsLogUtil.getInstance().info(tradeDetail.getPayOrderNumber()+"调用快钱代付原始数据=" + JSON.toJSONString(tradeDetail));
		Map<String, Object> resultMap = new HashMap<String,Object>();
		//生成pki加密报文
		String payPkiMsg = payPkiMsg(tradeDetail,membercode);
		if(StringUtils.isEmpty(payPkiMsg)) {
			resultMap.put("errorCode",MarvelsConstant.FAILED);
			resultMap.put("errorMsg","加密报文异常");
			return resultMap;
		}
		String sealMsg = invokeCSSCollection(payPkiMsg,url);
		if(StringUtils.isEmpty(sealMsg)) {
			resultMap.put("errorCode",MarvelsConstant.FAILED);
			resultMap.put("errorMsg","调用快钱支付接口异常");
			return resultMap;
		}
		//返回的加密报文解密
		Pay2bankResponse bankResponse = unPaySealMsg(membercode, sealMsg);
		if(null == bankResponse || null == bankResponse.getResponseBody()) {
			resultMap.put("errorCode",MarvelsConstant.FAILED);
			resultMap.put("errorMsg","调用快钱支付接口失败");
			return resultMap;
		}
		
		if(StringUtils.equals("0000", bankResponse.getResponseBody().getErrorCode())) {
			resultMap.put("errorCode",MarvelsConstant.SUCCESS);
			resultMap.put("errorMsg","代付交易成功");
		}else {
			resultMap.put("errorCode",MarvelsConstant.FAILED);
			resultMap.put("errorMsg",bankResponse.getResponseBody().getErrorMsg());
		}
		
		MarvelsLogUtil.getInstance().info(tradeDetail.getPayOrderNumber()+"调用快钱代付交易结果=" + JSON.toJSONString(resultMap));
		return resultMap;
	}
	
	public static Map<String, Object> payResultQuery(String membercode,String url,McfeRepaymentTradeDetails tradeDetail) {
		MarvelsLogUtil.getInstance().info(tradeDetail.getPayOrderNumber()+"调用快钱代付结果查询商户号=" + membercode+ ",请求地址="+url);
		MarvelsLogUtil.getInstance().info(tradeDetail.getPayOrderNumber()+"调用快钱代付结果查询原始数据=" + JSON.toJSONString(tradeDetail));
		Map<String, Object> resultMap = new HashMap<String,Object>();
		//生成pki加密报文
		String payPkiMsg = payQueryPkiMsg(tradeDetail,membercode);
		if(StringUtils.isEmpty(payPkiMsg)) {
			resultMap.put("result", "1");
			resultMap.put("errorCode",MarvelsConstant.FAILED);
			resultMap.put("errorMsg","查询交易结果加密报文失败");
			return resultMap;
		}
		String sealMsg;
		try {
			sealMsg = invokeCSSCollection(payPkiMsg,url);
		} catch (Exception e) {
			resultMap.put("result", "1");
			resultMap.put("errorCode",MarvelsConstant.FAILED);
			resultMap.put("errorMsg","查询交易结果发送请求失败");
			return resultMap;
		}
		//返回的加密报文解密
		try {
			resultMap = unPayQuerySealMsg(membercode, sealMsg);
		} catch (Exception e) {
			resultMap.put("result", "1");
			resultMap.put("errorCode",MarvelsConstant.FAILED);
			resultMap.put("errorMsg","查询交易结果解密失败");
			return resultMap;
		}

		MarvelsLogUtil.getInstance().info(tradeDetail.getPayOrderNumber()+"调用快钱交易结果查询返回=" + JSON.toJSONString(resultMap));
		return resultMap;
	}	
	
	private static Pay2bankResponse unPaySealMsg(String membercode, String msg) throws Exception {
		MarvelsLogUtil.getInstance().info("代付加密返回报文 = " + msg);
		Pay2bankResponse response = PayCCSUtil.converyToJavaBean(msg, Pay2bankResponse.class);
		SealedData sealedData = new SealedData();
		sealedData.setOriginalData(response.getResponseBody().getSealDataType().getOriginalData()==null?null:PKIUtil.utf8String2ByteWithBase64(response.getResponseBody().getSealDataType().getOriginalData()));
		sealedData.setSignedData(response.getResponseBody().getSealDataType().getSignedData()==null?null:PKIUtil.utf8String2ByteWithBase64(response.getResponseBody().getSealDataType().getSignedData()));
		sealedData.setEncryptedData(response.getResponseBody().getSealDataType().getEncryptedData()==null?null:PKIUtil.utf8String2ByteWithBase64(response.getResponseBody().getSealDataType().getEncryptedData()));
		sealedData.setDigitalEnvelope(response.getResponseBody().getSealDataType().getDigitalEnvelope()==null?null:PKIUtil.utf8String2ByteWithBase64(response.getResponseBody().getSealDataType().getDigitalEnvelope()));
		Mpf mpf = genMpf(fetureCode , membercode);
		UnsealedData unsealedData = null; 
		try {
			ICryptoService service = CryptoServiceFactory.createCryptoService();
			unsealedData = service.unseal(mpf, sealedData);
		} catch (CryptoException e) {
			MarvelsLogUtil.getInstance().error("解密报文异常 = ",e);
			return null;
		}
		byte[] decryptedData = unsealedData.getDecryptedData();
		String rtnString = "";
		if (null != decryptedData) {
			rtnString = PKIUtil.byte2UTF8String(decryptedData);
			MarvelsLogUtil.getInstance().info("解密代付后返回报文 = " + rtnString);
		} else {
			rtnString = PKIUtil.byte2UTF8String(sealedData.getOriginalData());
			MarvelsLogUtil.getInstance().info("解密代付后返回报文 = " + rtnString);
		}
		return response;
	}
	
	private static Map<String, Object> unPayQuerySealMsg(String membercode, String msg) throws Exception {
		MarvelsLogUtil.getInstance().info("交易结果查询加密返回报文 = " + msg);
		Pay2bankSearchResponse response = PayCCSUtil.converyToJavaBean(msg, Pay2bankSearchResponse.class);
		Map<String, Object> resultMap = new HashMap<String,Object>();
		//返回状态码为：订单号不存在时标记为失败
		if(StringUtils.equals("Q0018", response.getSearchResponseBody().getErrorCode())) {
			resultMap.put("result", "-1");
			resultMap.put("errorCode",response.getSearchResponseBody().getErrorCode());
			resultMap.put("errorMsg",response.getSearchResponseBody().getErrorMsg());
			return resultMap;
		}
		
		SealedData sealedData = new SealedData();
		sealedData.setOriginalData(response.getSearchResponseBody().getSealDataType().getOriginalData()==null?null:PKIUtil.utf8String2ByteWithBase64(response.getSearchResponseBody().getSealDataType().getOriginalData()));
		sealedData.setSignedData(response.getSearchResponseBody().getSealDataType().getSignedData()==null?null:PKIUtil.utf8String2ByteWithBase64(response.getSearchResponseBody().getSealDataType().getSignedData()));
		sealedData.setEncryptedData(response.getSearchResponseBody().getSealDataType().getEncryptedData()==null?null:PKIUtil.utf8String2ByteWithBase64(response.getSearchResponseBody().getSealDataType().getEncryptedData()));
		sealedData.setDigitalEnvelope(response.getSearchResponseBody().getSealDataType().getDigitalEnvelope()==null?null:PKIUtil.utf8String2ByteWithBase64(response.getSearchResponseBody().getSealDataType().getDigitalEnvelope()));
		Mpf mpf = genMpf(fetureCode , membercode);
		UnsealedData unsealedData = null;
		try {
			ICryptoService service = CryptoServiceFactory.createCryptoService();
			unsealedData = service.unseal(mpf, sealedData);
		} catch (CryptoException e) {
			MarvelsLogUtil.getInstance().error("解密报文异常 = ",e);
			return null;
		}
		byte[] decryptedData = unsealedData.getDecryptedData();
		String rtnString = "";
		if (null != decryptedData) {
			rtnString = PKIUtil.byte2UTF8String(decryptedData);
		} else {
			rtnString = PKIUtil.byte2UTF8String(sealedData.getOriginalData());
		}
		MarvelsLogUtil.getInstance().info("交易结果查询解密后返回报文 = " + rtnString);
		Pay2bankSearchResult result = PayCCSUtil.converyToJavaBean(rtnString,Pay2bankSearchResult.class);
		Pay2bankSearchDetail payResult = result.getResultList().get(0);
		String status = payResult.getStatus();
		String resultCode = "1";
		String resultMsg = "代付结果查询中";
		//111成功，112失败
		if(StringUtils.equals("111", status)) {
			resultCode = "0";
			resultMsg = "放款成功";
		}else if(StringUtils.equals("112", status)) {
			resultCode = "-1";
			resultMsg = StringUtils.isEmpty(payResult.getErrorMsg()) ? "放款失败":payResult.getErrorMsg();
		}
		
		resultMap.put("result", resultCode);
		resultMap.put("errorCode",MarvelsConstant.SUCCESS);
		resultMap.put("errorMsg",resultMsg);
		return resultMap;
	}	
	
	private static String payPkiMsg(McfeRepaymentTradeDetails tradeDetail,String memberCode) {
		//构建一个订单对象
		Pay2bankOrder orderDto = PayCCSUtil.genOrder(tradeDetail);
		String orderXml = PayCCSUtil.convertToXml(orderDto, encoding);
		MarvelsLogUtil.getInstance().info(tradeDetail.getPayOrderNumber()+"请求明文报文 = " + orderXml);
		//获取原始报文
		String originalStr = orderXml;
		//加签、加密
		Mpf mpf = genMpf(fetureCode , memberCode);
		SealedData sealedData = null;
		try {
			ICryptoService service = CryptoServiceFactory.createCryptoService();
			sealedData = service.seal(mpf, originalStr.getBytes());
		} catch (CryptoException e) {
			MarvelsLogUtil.getInstance().error(tradeDetail.getPayOrderNumber()+"加密报文异常 = ",e);
			return "";
		}
		Pay2bankRequest request = PayCCSUtil.genRequest(memberCode , VERSION);
		byte[] nullbyte = {};
		byte[] byteOri = sealedData.getOriginalData() == null ? nullbyte : sealedData.getOriginalData();
		byte[] byteEnc = sealedData.getEncryptedData() == null ? nullbyte : sealedData.getEncryptedData();
		byte[] byteEnv = sealedData.getDigitalEnvelope() == null ? nullbyte : sealedData.getDigitalEnvelope();
		byte[] byteSig = sealedData.getSignedData() == null ? nullbyte : sealedData.getSignedData();
		request.getRequestBody().getSealDataType().setOriginalData(PKIUtil.byte2UTF8StringWithBase64(byteOri));
		//获取加签报文
		request.getRequestBody().getSealDataType().setSignedData(PKIUtil.byte2UTF8StringWithBase64(byteSig));
		//获取加密报文
		request.getRequestBody().getSealDataType().setEncryptedData(PKIUtil.byte2UTF8StringWithBase64(byteEnc));
		//数字信封
		request.getRequestBody().getSealDataType().setDigitalEnvelope(PKIUtil.byte2UTF8StringWithBase64(byteEnv));
		
		//请求报文
		String requestXml = PayCCSUtil.convertToXml(request, encoding);
		MarvelsLogUtil.getInstance().info(tradeDetail.getPayOrderNumber()+"请求加密报文 = " + requestXml);
		return requestXml;
	}
	
	private static String payQueryPkiMsg(McfeRepaymentTradeDetails tradeDetail,String memberCode) {
		//构建一个订单对象
		Pay2bankSearchRequestParam orderDto = PayCCSUtil.payResultQueryParam(tradeDetail);
		String orderXml = PayCCSUtil.convertToXml(orderDto, encoding);
		MarvelsLogUtil.getInstance().info(tradeDetail.getPayOrderNumber()+"查询交易结果明文报文 = " + orderXml);
		//获取原始报文
		String originalStr = orderXml;
		//加签、加密
		Mpf mpf = genMpf(fetureCode , memberCode);
		SealedData sealedData = null;
		try {
			ICryptoService service = CryptoServiceFactory.createCryptoService();
			sealedData = service.seal(mpf, originalStr.getBytes());
		} catch (CryptoException e) {
			MarvelsLogUtil.getInstance().info(tradeDetail.getPayOrderNumber()+"获取密文失败");
		}
		Pay2bankSearchRequest request = PayCCSUtil.genPayQueryRequest(memberCode , VERSION);
		byte[] nullbyte = {};
		byte[] byteOri = sealedData.getOriginalData() == null ? nullbyte : sealedData.getOriginalData();
		byte[] byteEnc = sealedData.getEncryptedData() == null ? nullbyte : sealedData.getEncryptedData();
		byte[] byteEnv = sealedData.getDigitalEnvelope() == null ? nullbyte : sealedData.getDigitalEnvelope();
		byte[] byteSig = sealedData.getSignedData() == null ? nullbyte : sealedData.getSignedData();
		request.getSearchRequestBody().getSealDataType().setOriginalData(PKIUtil.byte2UTF8StringWithBase64(byteOri));
		//获取加签报文
		request.getSearchRequestBody().getSealDataType().setSignedData(PKIUtil.byte2UTF8StringWithBase64(byteSig));
		//获取加密报文
		request.getSearchRequestBody().getSealDataType().setEncryptedData(PKIUtil.byte2UTF8StringWithBase64(byteEnc));
		//数字信封
		request.getSearchRequestBody().getSealDataType().setDigitalEnvelope(PKIUtil.byte2UTF8StringWithBase64(byteEnv));
		
		//请求报文
		String requestXml = PayCCSUtil.convertToXml(request, encoding);
		MarvelsLogUtil.getInstance().info(tradeDetail.getPayOrderNumber()+"查询交易结果加密报文 = " + requestXml);
		return requestXml;
	}	
	
	private static String invokeCSSCollection(String requestXml,String url) throws Exception {
		//初始化HttpClient
		HttpClient client = new HttpClient();
		PostMethod method = new PostMethod(url);
		SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
	        sslContext.init(null, null, null);
	        SSLContext.setDefault(sslContext);
		// url的连接等待超时时间设置  
        client.getHttpConnectionManager().getParams().setConnectionTimeout(20000);  

        // 读取数据超时时间设置  
        client.getHttpConnectionManager().getParams().setSoTimeout(30000);  
		method.setRequestEntity(new StringRequestEntity(requestXml, "text/html", "utf-8"));
		client.executeMethod(method);
		
		//打印服务器返回的状态
		MarvelsLogUtil.getInstance().info("调用快钱返回http状态="+method.getStatusLine());
		
		//打印返回的信息
		InputStream stream = method.getResponseBodyAsStream();
		BufferedReader br = new BufferedReader(new InputStreamReader(stream,encoding));
		StringBuffer buf = new StringBuffer();
		String line;
		while (null != (line = br.readLine())) {
			buf.append(line).append("\n");
		}
		//释放连接
		String resultXml = buf.toString();
		MarvelsLogUtil.getInstance().info("调用快钱返回报文="+resultXml);
		method.releaseConnection();
		return resultXml;
	}
	
	public static Mpf genMpf(String fetureCode, String membercode) {
		Mpf mpf = new Mpf();
		mpf.setFeatureCode(fetureCode);
		mpf.setMemberCode(membercode);
		return mpf;
	}
}